Skip to content

Conversation

@kon28289
Copy link
Contributor

@kon28289 kon28289 commented Jul 29, 2025

What is this PR?🔍

Changes💻

  • 토큰 재발급 시 JWT 필터를 거쳐 발생하는 오류를 수정합니다.

Summary by CodeRabbit

  • 신규 기능

    • 회원 인증 및 이메일 관련 일부 엔드포인트에 대해 인증 없이 접근이 가능하도록 변경되었습니다.
  • 버그 수정

    • JWT 인증 필터가 회원 인증 및 이메일 관련 엔드포인트에는 적용되지 않도록 예외 처리가 추가되었습니다.
  • 기타

    • 이력서 이미지 URL 처리 로직이 개선되어 항상 최신 presigned URL을 사용하도록 변경되었습니다.
    • 내부 리소스 참조가 최신 상태로 업데이트되었습니다.

@kon28289 kon28289 requested review from Juhye0k and patulus July 29, 2025 12:28
@kon28289 kon28289 added the bug Something isn't working label Jul 29, 2025
@coderabbitai
Copy link

coderabbitai bot commented Jul 29, 2025

Walkthrough

HTTP 보안 설정과 JWT 인증 필터에서 회원 인증 및 이메일 관련 엔드포인트를 명시적으로 인증 없이 접근 가능하도록 변경했습니다. 이와 함께 이력서 서비스에서 이미지 URL 처리 로직을 단순화했으며, 리소스 프로퍼티의 서브프로젝트 커밋 참조가 갱신되었습니다.

Changes

Cohort / File(s) Change Summary
보안 설정 명시화
src/main/java/kr/ac/kumoh/d138/JobForeigner/global/config/security/SecurityConfig.java
회원 인증 및 이메일 관련 엔드포인트(/api/v1/members/sign-in, /api/v1/members/sign-up/**, /api/v1/members/refresh, /api/v1/email/**)에 대해 인증 없이 접근 가능하도록 명시적으로 허용하는 보안 설정 추가. 기존 로직은 유지.
JWT 인증 필터 예외 경로 추가
src/main/java/kr/ac/kumoh/d138/JobForeigner/global/jwt/filter/JwtAuthenticationFilter.java
JWT 인증 필터에서 특정 엔드포인트에 대해 필터링을 건너뛰도록 예외 경로를 명시적으로 추가. Authorization 헤더가 없거나 예외 경로에 해당하면 필터 미적용.
이력서 이미지 URL 처리 단순화
src/main/java/kr/ac/kumoh/d138/JobForeigner/resume/service/ResumeService.java
updateResume 메서드에서 이미지 URL을 항상 presigned URL로 사용하도록 로직 단순화. 조건문 제거 및 순서 변경.
프로퍼티 서브모듈 커밋 참조 갱신
src/main/resources/properties
서브프로젝트 커밋 해시를 최신 값으로 갱신. 그 외 변경 없음.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~8 minutes

Note

⚡️ Unit Test Generation is now available in beta!

Learn more here, or try it out under "Finishing Touches" below.

✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch fix/refresh

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai generate unit tests to generate unit tests for this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 93cb643 and 9f795cf.

📒 Files selected for processing (4)
  • src/main/java/kr/ac/kumoh/d138/JobForeigner/global/config/security/SecurityConfig.java (1 hunks)
  • src/main/java/kr/ac/kumoh/d138/JobForeigner/global/jwt/filter/JwtAuthenticationFilter.java (2 hunks)
  • src/main/java/kr/ac/kumoh/d138/JobForeigner/resume/service/ResumeService.java (1 hunks)
  • src/main/resources/properties (1 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
**/*.java

⚙️ CodeRabbit Configuration File

**/*.java: This project follows key rules from the NAVER Campus Hackday Java Coding Conventions (partial summary below; see full spec for details). * Indentation: use spaces (no tabs), tab width = 4 spaces. Files must end with Unix LF newline. * Line length: maximum 120 characters. * Imports: single-class imports only, allow wildcard for static imports, group imports with blank lines between sections. * Assignment operators (=, +=, -=, etc.): always have one space before and after; when breaking lines, the operator must start the next line (Binary expressions: operation sign on next line). * Lambda expressions: omit parentheses for a single parameter; surround -> with spaces (param -> expression); use braces and explicit return for multi-statement bodies; choose short, clear parameter names to keep intent visible. For the complete set of conventions and detailed examples, please refer to the full spec at https://naver.github.io/hackday-conventions-java/.

Files:

  • src/main/java/kr/ac/kumoh/d138/JobForeigner/resume/service/ResumeService.java
  • src/main/java/kr/ac/kumoh/d138/JobForeigner/global/config/security/SecurityConfig.java
  • src/main/java/kr/ac/kumoh/d138/JobForeigner/global/jwt/filter/JwtAuthenticationFilter.java
🧠 Learnings (3)
📓 Common learnings
Learnt from: patulus
PR: th-D138/Backend-JobForeigner#32
File: src/main/java/kr/ac/kumoh/d138/JobForeigner/email/domain/AuthCode.java:21-32
Timestamp: 2025-05-09T13:04:36.837Z
Learning: AuthCode 엔티티에 있는 만료된 레코드(expired_at이 경과된 레코드)를 삭제하는 작업은 다음 PR에서 구현될 예정입니다.
src/main/java/kr/ac/kumoh/d138/JobForeigner/global/config/security/SecurityConfig.java (2)

Learnt from: patulus
PR: #59
File: src/main/java/kr/ac/kumoh/d138/JobForeigner/global/jwt/authentication/JwtAuthentication.java:24-24
Timestamp: 2025-07-10T01:43:40.239Z
Learning: Spring Security의 hasRole() 및 hasAnyRole() 메서드는 자동으로 "ROLE_" 접두사를 추가하므로, SecurityConfig에서 MemberType.ADMIN.name()을 사용하고 JwtAuthentication에서 memberType.getKey()를 사용하는 것이 호환됨. SecurityConfig 수정이 불필요함.

Learnt from: patulus
PR: #32
File: src/main/java/kr/ac/kumoh/d138/JobForeigner/member/service/CompanyMemberService.java:64-67
Timestamp: 2025-05-12T01:00:17.875Z
Learning: AuthEmailService는 회원 검증과 이메일 인증 관련 비즈니스 예외(MEMBER_NOT_FOUND, EMAIL_ALREADY_VERIFIED, AUTH_CODE_INVALID)를 발생시키며, 이 예외들은 호출 서비스로 전파된다.

src/main/java/kr/ac/kumoh/d138/JobForeigner/global/jwt/filter/JwtAuthenticationFilter.java (1)

Learnt from: patulus
PR: #59
File: src/main/java/kr/ac/kumoh/d138/JobForeigner/global/jwt/authentication/JwtAuthentication.java:24-24
Timestamp: 2025-07-10T01:43:40.239Z
Learning: Spring Security의 hasRole() 및 hasAnyRole() 메서드는 자동으로 "ROLE_" 접두사를 추가하므로, SecurityConfig에서 MemberType.ADMIN.name()을 사용하고 JwtAuthentication에서 memberType.getKey()를 사용하는 것이 호환됨. SecurityConfig 수정이 불필요함.

🔇 Additional comments (3)
src/main/resources/properties (1)

1-1: 서브모듈 커밋 업데이트 확인 필요

서브프로젝트 커밋 해시만 변경되었습니다. 로컬 및 CI 환경 모두에서 git submodule update --init --recursive 커맨드로 실제 서브모듈이 해당 커밋으로 체크아웃되는지 확인해 주세요. 누락되면 빌드가 실패할 수 있습니다.

src/main/java/kr/ac/kumoh/d138/JobForeigner/global/config/security/SecurityConfig.java (1)

54-57: 토큰 재발급 시 JWT 필터 오류 수정을 위한 적절한 보안 설정

토큰 재발급(/api/v1/members/refresh)을 포함하여 인증이 필요하지 않은 엔드포인트들을 명시적으로 permitAll()로 설정한 것이 올바릅니다. 이는 PR의 주요 목적인 "토큰 재발급 시 JWT 필터를 거쳐 오류가 발생하는 문제"를 해결하는 핵심적인 변경사항입니다.

  • /api/v1/members/sign-in: 로그인 엔드포인트
  • /api/v1/members/sign-up/**: 회원가입 관련 엔드포인트
  • /api/v1/members/refresh: 토큰 재발급 엔드포인트 (핵심)
  • /api/v1/email/**: 이메일 인증 관련 엔드포인트
src/main/java/kr/ac/kumoh/d138/JobForeigner/global/jwt/filter/JwtAuthenticationFilter.java (1)

36-37: JWT 필터 제외 로직이 올바르게 구현됨

shouldNotFilter 메서드의 로직이 올바르게 수정되었습니다. Authorization 헤더가 없거나 제외 경로에 해당하는 요청은 JWT 필터를 건너뛰도록 하여, SecurityConfig의 설정과 일치하게 동작합니다. 이는 토큰 재발급 시 JWT 필터 오류를 해결하는 핵심 로직입니다.

@kon28289 kon28289 merged commit 7cb74f2 into dev Jul 29, 2025
3 checks passed
@kon28289 kon28289 deleted the fix/refresh branch July 29, 2025 12:35
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 93cb643 and 9f795cf.

📒 Files selected for processing (4)
  • src/main/java/kr/ac/kumoh/d138/JobForeigner/global/config/security/SecurityConfig.java (1 hunks)
  • src/main/java/kr/ac/kumoh/d138/JobForeigner/global/jwt/filter/JwtAuthenticationFilter.java (2 hunks)
  • src/main/java/kr/ac/kumoh/d138/JobForeigner/resume/service/ResumeService.java (1 hunks)
  • src/main/resources/properties (1 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
**/*.java

⚙️ CodeRabbit Configuration File

**/*.java: This project follows key rules from the NAVER Campus Hackday Java Coding Conventions (partial summary below; see full spec for details). * Indentation: use spaces (no tabs), tab width = 4 spaces. Files must end with Unix LF newline. * Line length: maximum 120 characters. * Imports: single-class imports only, allow wildcard for static imports, group imports with blank lines between sections. * Assignment operators (=, +=, -=, etc.): always have one space before and after; when breaking lines, the operator must start the next line (Binary expressions: operation sign on next line). * Lambda expressions: omit parentheses for a single parameter; surround -> with spaces (param -> expression); use braces and explicit return for multi-statement bodies; choose short, clear parameter names to keep intent visible. For the complete set of conventions and detailed examples, please refer to the full spec at https://naver.github.io/hackday-conventions-java/.

Files:

  • src/main/java/kr/ac/kumoh/d138/JobForeigner/global/config/security/SecurityConfig.java
  • src/main/java/kr/ac/kumoh/d138/JobForeigner/global/jwt/filter/JwtAuthenticationFilter.java
  • src/main/java/kr/ac/kumoh/d138/JobForeigner/resume/service/ResumeService.java
🧠 Learnings (3)
📓 Common learnings
Learnt from: patulus
PR: th-D138/Backend-JobForeigner#32
File: src/main/java/kr/ac/kumoh/d138/JobForeigner/email/domain/AuthCode.java:21-32
Timestamp: 2025-05-09T13:04:36.837Z
Learning: AuthCode 엔티티에 있는 만료된 레코드(expired_at이 경과된 레코드)를 삭제하는 작업은 다음 PR에서 구현될 예정입니다.
src/main/java/kr/ac/kumoh/d138/JobForeigner/global/config/security/SecurityConfig.java (2)

Learnt from: patulus
PR: #59
File: src/main/java/kr/ac/kumoh/d138/JobForeigner/global/jwt/authentication/JwtAuthentication.java:24-24
Timestamp: 2025-07-10T01:43:40.239Z
Learning: Spring Security의 hasRole() 및 hasAnyRole() 메서드는 자동으로 "ROLE_" 접두사를 추가하므로, SecurityConfig에서 MemberType.ADMIN.name()을 사용하고 JwtAuthentication에서 memberType.getKey()를 사용하는 것이 호환됨. SecurityConfig 수정이 불필요함.

Learnt from: patulus
PR: #32
File: src/main/java/kr/ac/kumoh/d138/JobForeigner/member/service/CompanyMemberService.java:64-67
Timestamp: 2025-05-12T01:00:17.875Z
Learning: AuthEmailService는 회원 검증과 이메일 인증 관련 비즈니스 예외(MEMBER_NOT_FOUND, EMAIL_ALREADY_VERIFIED, AUTH_CODE_INVALID)를 발생시키며, 이 예외들은 호출 서비스로 전파된다.

src/main/java/kr/ac/kumoh/d138/JobForeigner/global/jwt/filter/JwtAuthenticationFilter.java (1)

Learnt from: patulus
PR: #59
File: src/main/java/kr/ac/kumoh/d138/JobForeigner/global/jwt/authentication/JwtAuthentication.java:24-24
Timestamp: 2025-07-10T01:43:40.239Z
Learning: Spring Security의 hasRole() 및 hasAnyRole() 메서드는 자동으로 "ROLE_" 접두사를 추가하므로, SecurityConfig에서 MemberType.ADMIN.name()을 사용하고 JwtAuthentication에서 memberType.getKey()를 사용하는 것이 호환됨. SecurityConfig 수정이 불필요함.

🔇 Additional comments (2)
src/main/resources/properties (1)

1-1: 서브모듈 커밋 SHA 갱신 시 파이프라인 정상 동작 검증 권장

properties 파일의 서브모듈 커밋 해시가 11b82a2…로 변경되었습니다. 실제 서브모듈 디렉터리가 이 SHA로 체크아웃되어 있고 CI/CD 파이프라인에서 git submodule update --init --recursive 과정이 정상 동작하는지 확인해 주세요. 해시 불일치 시 빌드 실패가 발생할 수 있습니다.

src/main/java/kr/ac/kumoh/d138/JobForeigner/global/config/security/SecurityConfig.java (1)

54-57: LGTM! 토큰 재발급 관련 엔드포인트 접근 권한이 적절히 설정되었습니다.

회원 인증 관련 엔드포인트들(sign-in, sign-up, refresh)과 이메일 관련 엔드포인트를 permitAll()로 설정한 것은 올바른 접근입니다. 이러한 엔드포인트들은 본질적으로 인증이 필요하지 않은 공개 API이므로 보안상 문제가 없습니다.


@Override
protected boolean shouldNotFilter(HttpServletRequest request) throws ServletException {
String[] excludePath = {"api/v1/members/sign-in", "api/v1/members/sign-up/**", "/api/v1/members/refresh", "/api/v1/email/**"};
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

경로 일관성 문제를 수정하세요.

excludePath 배열에서 경로의 일관성이 맞지 않습니다. 일부 경로는 "/" 접두사가 있고 일부는 없습니다.

다음과 같이 모든 경로에 "/" 접두사를 추가하세요:

-        String[] excludePath = {"api/v1/members/sign-in", "api/v1/members/sign-up/**", "/api/v1/members/refresh", "/api/v1/email/**"};
+        String[] excludePath = {"/api/v1/members/sign-in", "/api/v1/members/sign-up", "/api/v1/members/refresh", "/api/v1/email"};

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In
src/main/java/kr/ac/kumoh/d138/JobForeigner/global/jwt/filter/JwtAuthenticationFilter.java
at line 33, the excludePath array has inconsistent path prefixes, with some
paths starting with "/" and others not. To fix this, ensure all paths in the
excludePath array consistently start with a "/" prefix by adding it to those
missing it.

Comment on lines +36 to +37
return authorizationHeader == null
|| Arrays.stream(excludePath).anyMatch(request.getRequestURI()::startsWith);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

startsWith 사용으로 인한 보안 취약점을 수정하세요.

현재 startsWith 방식은 의도하지 않은 경로까지 매치할 수 있는 보안 문제가 있습니다. 예를 들어 "/api/v1/members/sign-in-test" 같은 경로도 매치됩니다.

정확한 패턴 매칭을 위해 다음과 같이 수정하세요:

-        return authorizationHeader == null
-                || Arrays.stream(excludePath).anyMatch(request.getRequestURI()::startsWith);
+        String requestURI = request.getRequestURI();
+        return authorizationHeader == null
+                || requestURI.equals("/api/v1/members/sign-in")
+                || requestURI.startsWith("/api/v1/members/sign-up/")
+                || requestURI.equals("/api/v1/members/refresh")
+                || requestURI.startsWith("/api/v1/email/");
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
return authorizationHeader == null
|| Arrays.stream(excludePath).anyMatch(request.getRequestURI()::startsWith);
String requestURI = request.getRequestURI();
return authorizationHeader == null
|| requestURI.equals("/api/v1/members/sign-in")
|| requestURI.startsWith("/api/v1/members/sign-up/")
|| requestURI.equals("/api/v1/members/refresh")
|| requestURI.startsWith("/api/v1/email/");
🤖 Prompt for AI Agents
In
src/main/java/kr/ac/kumoh/d138/JobForeigner/global/jwt/filter/JwtAuthenticationFilter.java
around lines 36 to 37, the use of startsWith for path matching causes security
issues by matching unintended paths. Replace the startsWith check with an exact
match or a more precise pattern matching method to ensure only the intended
paths in excludePath are matched, preventing accidental matches like
"/api/v1/members/sign-in-test".

Resume resume = resumeRepository.findById(resumeId)
.orElseThrow(()-> new BusinessException(ExceptionType.RESUME_NOT_FOUND));

String imageUrl = resumeImageService.getResumeImagePresignedUrl(memberId);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

권한 체크 후에 이미지 URL을 생성하세요.

권한이 확인되지 않은 상태에서 resumeImageService.getResumeImagePresignedUrl(memberId)를 호출하는 것은 보안상 좋지 않습니다. 해당 사용자가 실제로 이력서의 소유자인지 확인한 후에 이미지 URL을 생성해야 합니다.

다음과 같이 순서를 변경하세요:

     @Transactional
     public ResumeResponse updateResume(ResumeRequest request, Long memberId, Long resumeId) {
         Resume resume = resumeRepository.findById(resumeId)
                 .orElseThrow(()-> new BusinessException(ExceptionType.RESUME_NOT_FOUND));

-        String imageUrl = resumeImageService.getResumeImagePresignedUrl(memberId);
         if(!resume.getMember().getId().equals(memberId)) {
             throw new BusinessException(ExceptionType.RESUME_FORBIDDEN);
         }
+        
+        String imageUrl = resumeImageService.getResumeImagePresignedUrl(memberId);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
String imageUrl = resumeImageService.getResumeImagePresignedUrl(memberId);
@Transactional
public ResumeResponse updateResume(ResumeRequest request, Long memberId, Long resumeId) {
Resume resume = resumeRepository.findById(resumeId)
.orElseThrow(() -> new BusinessException(ExceptionType.RESUME_NOT_FOUND));
if (!resume.getMember().getId().equals(memberId)) {
throw new BusinessException(ExceptionType.RESUME_FORBIDDEN);
}
String imageUrl = resumeImageService.getResumeImagePresignedUrl(memberId);
// ... the rest of the method ...
}
🤖 Prompt for AI Agents
In src/main/java/kr/ac/kumoh/d138/JobForeigner/resume/service/ResumeService.java
at line 112, the code calls
resumeImageService.getResumeImagePresignedUrl(memberId) before verifying the
user's authorization. To fix this, first perform a permission check to confirm
that the user is the owner of the resume, and only after successful
authorization, call getResumeImagePresignedUrl to generate the image URL. This
ensures secure access control before generating sensitive URLs.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants